// OGoMojo
// Whitemice.MOGI.MOGIMon
// License: LGPL
// Copyright: Whitemice Consulting, 2006
// Author(s): Adam Tauno Williams (awilliam@whitemice.org)
using System;
using log4net;
using System.IO;
using System.Data;
using Mono.Data.SqliteClient;

namespace Whitemice.MOGI.MOGIMon
{
	/// \brief Class that keeps track of work done
	/// \note Uses the MOGIMon.RecordFile directive from the confguration as the
	/// location for the SQLite book keeping database and the MOGIMon.MinimumEntry
	/// directive to determine the earliest audit entry to be processed if the
	/// book keeping database is not found and has to be created.  If the database
	/// specified cannot be found this class will automatically create it.
	public class RecordKeeper
	{
		IServer			server;
		IDbConnection	connection;
		
		/// \brief Constructor
		/// \param server Reference to OpenGroupware server
		public RecordKeeper(IServer server)
		{
		 server.Log.Debug("RecordKeeper object created");
			this.server = server;
		}
		
		public void Connect()
		{
			if(File.Exists(server.GetDirectiveValue("MOGIMon", "RecordFile")))
			{
				Log.Debug("Preexisting database found, connecting.");
				ConnectDatabase();
			}
				else 
				{
					Log.Info("No database found, creating.");
					CreateDatabase();
				}
		}
		
		public void Disconnect()
		{
			server.Log.Debug("Disconnecting from record keeper database.");
			connection.Close();
			connection = null;
		}
		
		/// \brief Setup connection to the SQLite book keeping database
		protected void ConnectDatabase()
		{
			Log.Debug("Connecting to record keeper database");
			connection = 
				(IDbConnection) new SqliteConnection(
					"URI=file:" +
					server.GetDirectiveValue("MOGIMon", "RecordFile"));
			try 
			{
				connection.Open();
			} catch (Exception ex)
				{ 
					Log.Fatal("Exception connecting to record keeping database.");
					Log.Fatal(ex.Message);
				}
			if (connection == null)
				Log.Fatal("RecordKeeper database connection is null after connect.");
		}
		
		/// \brief Create the SQLLite book keeping database
		protected void CreateDatabase()
		{
			IDbCommand command;
			
			ConnectDatabase();
			Log.Info("Creating record keeper database schema");
			command = connection.CreateCommand();
			command.CommandText = 
				"CREATE TABLE last_event_log (" +
				"  entry_id	INT);";
			command.ExecuteNonQuery();
      command.Dispose();
      command = null;
 			command = connection.CreateCommand();
			command.CommandText = 
				"INSERT INTO last_event_log (entry_id)" +
				"  VALUES(" + server.GetDirectiveValue("MOGIMon", "MinimumEntry") + ");";
			command.ExecuteNonQuery();
      command.Dispose();
      command = null;
      			command = connection.CreateCommand();
			command.CommandText = 
				"CREATE TABLE entityType (" +
				"  objectId	INT, " + 
				"  objectName CHAR(45));";
			command.ExecuteNonQuery();
      command.Dispose();
      command = null;

		}
		
		/// \brief Return id of last successfully processed audit/log entry
		/// \note This routine executes "SELECT entry_id FROM last_event_log;"
		/// against the book keeping database.
		/// \warn If this database query returns no rows the value returned
		/// will be zero, meaning all entries in audit log will be processed,
		/// this case should not occurr, but if it does you could see A LOT
		/// of activity.
		public int LastProcessedEntry
		{
			get
			{
				IDbCommand	command;
				IDataReader	reader;
				int			entry_id;
				
				Log.Debug("Retrieving last processed entry from record keeper");
				entry_id = 0;
				if (connection == null)
					Log.Fatal("RecordKeeper database connection is NULL in LastProcessedEntry");
				command = connection.CreateCommand();
				command.CommandText = "SELECT entry_id FROM last_event_log;";
				reader = command.ExecuteReader();
				if (reader == null)
					Log.Fatal("DataReader is null after command execution");
				while(reader.Read())
            		entry_id = reader.GetInt32(0);
            	reader.Close();
            	reader = null;
            	command.Dispose();
            	command = null;
        return entry_id;
			}
		}
		
		/// \brief Store processed entry in book keeping database
		/// \param entry_id Audit Id of successfully processed entries
		/// \note This only stores the provided value if that id is 
		/// greater than the LastProcessedEntry, otherwise the action
		/// is silently ignored.
		public void PutProcessedEntry(int entry_id)
		{
			IDbCommand command;
			
			if(entry_id > LastProcessedEntry)
			{
				Log.Debug("Storing processed entry id " + entry_id);
				command = connection.CreateCommand();
				command.CommandText = 
					"UPDATE last_event_log " +
					"  SET entry_id = " + entry_id.ToString() + ";";
				command.ExecuteNonQuery();
            	command.Dispose();
            	command = null;
			}
		}
		
		public string EntityName(int primaryKey) {
				IDbCommand	command;
				IDataReader	reader;
				string			entityName;
				
				Log.Debug("Determining entityName");
				entityName = null;
				if (connection == null)
					Log.Fatal("RecordKeeper database connection is NULL in EntityName");				
				command = connection.CreateCommand();
				command.CommandText = "SELECT objectName FROM entityType WHERE objectId = " + 
				                       primaryKey.ToString() + ";";
				reader = command.ExecuteReader();
				while(reader.Read())
					entityName = reader.GetString(0);
				reader.Close();
				reader = null;
				command.Dispose(); command = null;
				if (entityName != null)
				{
					Log.Debug("Entity name was retrieved from cache");
					return entityName;
				}
				Log.Debug("Entity name not found in cache, asking server.");
				try
				{
					entityName = server.GetName(primaryKey.ToString());
				} catch (NoTypeException ex)
					{
						Log.Info("Unable to determine type of reference entity");
						Log.Info(ex.Message);
						entityName = null;
					}
				if (entityName != null)
				{
					command = connection.CreateCommand();
					command.CommandText = "INSERT INTO entityType (objectId, objectName)" + 
				  	                    "  VALUES(" + primaryKey.ToString() + ", '" +
				  	                    entityName + "');";
					command.ExecuteNonQuery();				
					command.Dispose(); command = null;
					Log.Debug("Entity name retrieved from server and stored in cache");
				}
				return entityName;
		}
		
		public ILog Log
		{
			get { return server.Log; }
		}
	}
}